#pragma once
#include "stdafx.h"
#include <stdlib.h>
#include "ASN.h"
#include <time.h>

using namespace std;
#define TAG_BOOLEAN 0x01
#define TAG_INTEGER 0x02
#define TAG_BIT_STRING 0x03
#define TAG_OCTET_STRING 0x04
#define TAG_NULL 0x05
#define TAG_OBJECT_IDENTIFIER 0x06
#define TAG_EXTERNAL 0x08
#define TAG_INSTANCE_OF 0x08
#define TAG_REAL 0x09
#define TAG_UTF8STRING 0x0C
#define TAG_APPLICATION_CONSTRUCTED 0x60
#define TAG_APPLICATION 0x40
#define TAG_SEQUENCE 0x30
#define TAG_SEQUENCE_OF 0x30
#define TAG_CONTEXT_SPECIFIC_CONSTRUCTED 0xA0
#define TAG_CONTEXT_SPECIFIC 0x80
#define TAG_SET_OF 0x31
#define TAG_SET 0x31
#define TAG_CHOICE 0x1A
#define TAG_CHARACTER 0x1D
#define TAG_EMBEDDED_PDV 0x0B
#define TAG_NUMERICSTRING 0x12
#define TAG_PRINTABLESTRING 0x13
#define TAG_TELETEXSTRING 0x14
#define TAG_VIDEOTEXSTRING 0x15
#define TAG_IA5STRING 0x16
#define TAG_TIME 0x17
#define TAG_GRAPHICSTRING 0x19
#define TAG_VISIBLESTRING 0x1a
#define TAG_GENERALSTRING 0x1b
#define TAG_UNIVERSALSTRING 0x1c
#define TAG_BMPSTRING 0x1e


/**Helper Method*/
BYTE* base128(unsigned long val, int *len)
{
	int i = 5, j;
	BYTE* b = new BYTE[6];
	do {
		b[i] = (BYTE)(val & 0x7F);
		val = val >> 7;
		if (i != 5) b[i] |= 0x80;
		i = i-1;
	} while (val > 0);
	
	for (j = 0; j < 5; j++) {
		if (i+j < 5)
			b[j] = b[i+j+1];
		else b[j] = 0;
	}
	*len = 5-i;
   	return b;
}

/** Default constructor for Application Specific DO */
ApplicationSpecific::ApplicationSpecific() : DO((WORD)TAG_APPLICATION) {
}

/** Constructor for Application Specific DO when the number of the application is specified
arg =  Application number 
eg: Application Specific [23] , arg = 23*/
ApplicationSpecific::ApplicationSpecific(int arg, WORD length_arg, const BYTE* buffer): 
	DO((arg & 0x1F)+ TAG_APPLICATION_CONSTRUCTED, length_arg, buffer) {
}

/** Constructor for constructed Application Specific DO
 The list of DOs building this Application Specific DO is passed as a DOlist in the arguments.
arg is the Application number
eg: Application Specific [23] , arg = 23*/
ApplicationSpecific::ApplicationSpecific(int arg, const list<DO>& collection):
	DO((arg & 0x1F)+ TAG_APPLICATION_CONSTRUCTED) {
	setConstructedValue(collection);
}

/**Default constructor for Sequence Do*/
Sequence::Sequence(): DO(TAG_SEQUENCE) {
}

/** Constructor for primitive Sequence DO 
eg:	BYTE b[2] = {0x00, 0x80};
	Sequence *s = new Sequence((WORD)2, b);
	Output: 30 02 00 80
*/
Sequence::Sequence(WORD length_arg, const BYTE* buffer): DO(TAG_SEQUENCE, length_arg, buffer) {
}

/** Constructor for building a Sequence of multiple DOs
The DOs building the sequence is passed as a DO list argument */
Sequence::Sequence(const list<DO>& collection): DO(TAG_SEQUENCE) {
	setConstructedValue(collection);
}

Sequence::Sequence(DO *d):DO(TAG_SEQUENCE) {
	appendDO(*d);
}

/**Default constructor for Object identifier*/
ObjectIdentifier::ObjectIdentifier(): DO(TAG_OBJECT_IDENTIFIER) {
}

/**Constructor to build Object Identifier DO, given the ID value as a string,
 eg:long od[] = {1,2,840,113549};
	ObjectIdentifier *oid = new ObjectIdentifier(od, 4);
*/
ObjectIdentifier::ObjectIdentifier(unsigned long* buffer, int len): DO(TAG_OBJECT_IDENTIFIER)
{
    	int i, l;
 	BYTE *res;
	if (len < 2) return;
	res = base128(buffer[0] * 40 + buffer[1],&l);
	appendValue(l, res);
	delete res;
	i = 2;
	while (i < len) {
		res = base128(buffer[i], &l);
		appendValue(l, res);
		i++;
		delete res;
	}
}

/** Default constructor for Context Specific DO */	
ContextSpecific::ContextSpecific(): DO(TAG_CONTEXT_SPECIFIC) {
}


/** Constructor for Context Specific DO when the number of the context is specified
arg =  Context Specific number 
eg: Context Specific [23] , arg = 23*/
ContextSpecific::ContextSpecific(int arg, WORD length_arg, const BYTE* buffer):
	DO((arg & 0x1F)+ TAG_CONTEXT_SPECIFIC_CONSTRUCTED, length_arg, buffer) {
}

/** Constructor to build Context Specific DO given the list of element DOs
arg = Context SPecific number 
eg: Context Specific [23] , arg = 23*/
ContextSpecific::ContextSpecific(int arg, const list<DO>& collection):
	DO((arg & 0x1F)+ TAG_CONTEXT_SPECIFIC_CONSTRUCTED) {
	setConstructedValue(collection);
}

/**Default constructor for Set Of DO*/
Set::Set(): DO(TAG_SET_OF) {
}

/**Constructor for primitive Set Of DO
eg:	BYTE b[2] = {0x00, 0x80};
	Set *s = new Set((WORD)2, b); 
	Output: 31 02 00 80
*/
Set::Set(WORD length_arg, const BYTE* buffer): DO(TAG_SET, length_arg, buffer) {
}

/** Constructor to build Set Of DO given the list of element DOs*/
Set::Set(const list<DO>& collection): DO(TAG_SET) {
    setConstructedValue(collection);
}

Set::Set(DO *d): DO(TAG_SET) {
	appendDO(*d);
}

/**Default Constructor for Integer DO */
ASNInteger::ASNInteger(): DO(TAG_INTEGER) {
}

/**Contructor for integer which takes 2s compliment of a integer as input and length
eg. 	integer 128 in hex is 0080
	BYTE b[2] = {0x00, 0x80};
	Integer *i = new Integer(b, 2);
	b contains the 2s compliment of the integer
	len = 2
	Output: 02 02 00 80
*/
ASNInteger::ASNInteger(const BYTE* buffer, int len): DO(TAG_INTEGER) {
	appendValue(len, buffer);
}

/** Constructor for Null */
Null::Null(): DO(TAG_NULL) {
}

/** Default Constructor for BitString DO */
BitString::BitString(): DO(TAG_BIT_STRING) {
}

/** Constructor for primitive Bit String DO
eg:	BYTE b[2] = {0x00, 0x80};
	BitString *bt = new BitString(2, b); 
	Output: 03 03 00 00 80
*/
BitString::BitString(int length_arg, const BYTE* buffer): DO(TAG_BIT_STRING)
{
	BYTE b = 0;
	appendValue(1, &b);
	appendValue(length_arg, buffer);
}

/** Constrcutor to build Bit String DO from a list of DOs */
BitString::BitString(const list<DO>& collection): DO(TAG_BIT_STRING)
{
	BYTE b = 0;
	appendValue(1, &b);
	setConstructedValue(collection);
}

/**For primitive DO */
BitString::BitString(DO *d): DO(TAG_BIT_STRING) {
	appendDO(*d);
}


/**Default Constructor for OctetString DO*/
OctetString::OctetString(): DO(TAG_OCTET_STRING) {
}

/** Constrcutor for primtive Octet String DO 
eg:	BYTE b[2] = {0x00, 0x80};
	oid = new OctetString(2, b);
	Output: 04 02 00 80 
*/
OctetString::OctetString(int length_arg, const BYTE* buffer): DO(TAG_OCTET_STRING, length_arg, (BYTE*)buffer){
}

/** Constrcutor to build Octet String DO from a list of DOs */
OctetString::OctetString(const list<DO>& collection):DO(TAG_OCTET_STRING)
{
	setConstructedValue(collection);
}

/**For primitive DO */
OctetString::OctetString(DO *d): DO(TAG_OCTET_STRING) {
	appendDO(*d);
}

/** Default Constructor for TimeStamp DO*/
TimeStamp::TimeStamp(): DO(TAG_TIME) {
}

/**Constructor of Timestamp DO givent the string containing time
Parameters: i, Time 
	i=0 => Time in UTC Format ie yymmddhhminsec
	i=1 => Time in localtime +- delay format ie yymmddhhminsec(+ or -)delay from UTCTime
	Delay format: hhmin
eg.  28th September 2007 10:13:54 at UTC time can be given as
TimeStamp *tm = new TimeStamp(0, "070928101354Z");
and in Local Time of India can be given as
TimeStamp *tm = new TimeStamp(1, "070928101354+0530");
*/
TimeStamp::TimeStamp(int i, const BYTE* buffer):DO(TAG_TIME)
{	

	if (!i) {
		appendValue(12, buffer);
		appendValue(1, (BYTE*)"Z");
	}
	else {
		appendValue(17, buffer);
	}
}

/**Constructor of Timestamp DO givent the time structure containing time
Parameters: i, struct tm *t 
	i=0 => Time in UTC Format ie yymmddhhminsec
	i=1 => Time in localtime +- delay format ie yymmddhhminsec(+ or -)delay from UTCTime
	t = time structure
Function calculates delay from UTCTime, if time passed corresponds to localtime.
eg. struct tm t;
	t.tm_sec = 10; 
	t.tm_min = 19;
	t.tm_hour = 2;
	t.tm_mday = 28;
	t.tm_mon = 9;
	t.tm_year = 7;
	UTCTime: TimeStamp *tm = new TimeStamp(0, &t);
	Localtime: TimeStamp *tm = new TimeStamp(0, &t);
*/	
TimeStamp::TimeStamp(int i, struct tm *t): DO(TAG_TIME)
{
	char s[2];
	sprintf(s, "%02d", t->tm_year);	
	appendValue((WORD)2, (BYTE*)s);
	sprintf(s, "%02d", t->tm_mon);	
	appendValue((WORD)2, (BYTE*)s);
	sprintf(s, "%02d", t->tm_mday);	
	appendValue((WORD)2, (BYTE*)s);
	sprintf(s, "%02d", t->tm_hour);	
	appendValue((WORD)2, (BYTE*)s);
	sprintf(s, "%02d", t->tm_min);	
	appendValue((WORD)2, (BYTE*)s);
	sprintf(s, "%02d", t->tm_sec);	
	appendValue((WORD)2, (BYTE*)s);
	
	if (!i) {//i=0 => UTCTime
		appendValue(1, (BYTE *)"Z");
	}
	else {
		tzset();
		int hrs = _timezone/3600;
		int min =  (abs(_timezone)%3600)/60;
		
		if (hrs >= 0) {
			sprintf(s, "%c", '-');
			appendValue(1, (BYTE*)s);
		}
		else { 
			sprintf(s, "%c", '+');
			appendValue(1, (BYTE*)s);
			
		}
			
		sprintf(s, "%02d", abs(hrs));	
		appendValue((WORD)2, (BYTE*)s);
		sprintf(s, "%02d", min);	
		appendValue((WORD)2, (BYTE*)s);
	}
}

/** Default Constructor for Boolean DO*/
ASNBoolean::ASNBoolean():DO(TAG_BOOLEAN) {
}

/** Constructor for primitive Boolean DO
eg:	BYTE c[1] = {0x80};
	Boolean *b = new Boolean(true, c); 
	Output: 01 01 80
	Boolean *b = new Boolean(false, c); 
	Output: 01 01 00
*/
ASNBoolean::ASNBoolean(bool b, BYTE *val):DO(TAG_BOOLEAN) {	
	BYTE t = 0;
	if(!b) appendValue(1, &t);
	else appendValue(1, val);
}

/** Default Constructor for Choice DO*/
Choice::Choice():DO(TAG_CHOICE) {
}

/** Constructor for primitive Choice DO*/
Choice::Choice(const list<DO>& collection):DO(TAG_CHOICE){
	setConstructedValue(collection);
}

Choice::Choice(DO *d):DO(TAG_CHOICE){
	appendDO(*d);
}

/** Default Constructor for Relative Object identifer DO*/
RelativeOid::RelativeOid():DO(TAG_OBJECT_IDENTIFIER){
}

/** Constructor for primitive Relative Object Identifer DO*/
RelativeOid::RelativeOid(unsigned long* buffer, int len):DO(TAG_OBJECT_IDENTIFIER){
	int i =0;
	int l;
	BYTE *res;
	while (i < len) {
		res = base128(buffer[i], &l);
		appendValue(l, res);
		delete res;
	}
}

/** Default Constructor for Character String DO*/
CharacterString::CharacterString():DO(TAG_CHARACTER){
}

/** Constructor for primitive Character String DO*/
CharacterString::CharacterString(const list<DO>& collection):DO(TAG_CHARACTER){
	setConstructedValue(collection);
}

/** Default Constructor for EmbeddedPDV DO*/
EmbeddedPDV::EmbeddedPDV():DO(TAG_EMBEDDED_PDV){
}

/** Constructor for primitive EmbeddedPDV DO*/
EmbeddedPDV::EmbeddedPDV(const list<DO>& collection):DO(TAG_EMBEDDED_PDV){
	setConstructedValue(collection);
}

/** Default Constructor for InstanceOf DO*/
InstanceOf::InstanceOf():DO(TAG_INSTANCE_OF){
}

/** Constructor for primitive InstanceOf DO*/
InstanceOf::InstanceOf(const list<DO>& collection):DO(TAG_INSTANCE_OF){
	setConstructedValue(collection);
}

/** Default Constructor for External DO*/
External::External():DO(TAG_EXTERNAL){
}

/** Constructor for primitive External DO*/
External::External(const list<DO>& collection):DO(TAG_EXTERNAL){
	setConstructedValue(collection);
}

/** Default Constructor for Tagged*/
Tagged::Tagged(){
}

/** Constructor for primitive Tagged DO
Parameters: i, newTag, DO *d
	i=0 => implicit tagging, tag of d passed is set to newTag
	i=1 => explicit tagging, new DO with tag as newTag, and DO appended is formed
eg:	do = 03 01 00
	Tagged *t = new Tagged(0, 4, do); 
	Output: 04 01 00
	Tagged *t = new Tagged(1, 4, do); 
	Output: 04 03 03 01 00
*/
Tagged::Tagged(int i, WORD tag, DO *d) : DO(tag){
	if (!i) {//implicit
		//doubt in encoding ASN1
		//setTag(tag + TAG_CONTEXT_SPECIFIC);
		appendValue(d->getLength(), d->getValue());
	}
	else {//i=1, explicit
		appendDO(*d);
	}
}

/** Default constrcutor for Real DO*/
ASNReal :: ASNReal():DO(TAG_REAL)
{
}

/** Constrcutor for Real DO given string pattern and representation type (NR)
NR = 1 : "3", "-1"
NR = 2 : "3.0", "-1.6"
NR = 3 : "3.0E1", "100E+87"
eg. Real *r = new Real (3, "3.0E+87")*/
ASNReal :: ASNReal(int NR, const char *buffer):DO(TAG_REAL)
{
   WORD len = strlen(buffer);
   BYTE b = (char)NR;
   appendValue(1, &b);
   appendValue(len, (BYTE *)buffer);
}

/**Constructor for Real Do, given (mantissa, base, exponent) form
eg. */
ASNReal :: ASNReal(int sign, int mantissa_len, BYTE *mantissa, int base, int exp_len, BYTE *exponent, int scale):DO(TAG_REAL)
{
   if(sign<0) sign = 0x70;
   else sign = 0x00;
   BYTE temp;
   BYTE first = 0x80;
   first = first + sign;

   //add base
   switch(base)
   {
   case 2 : first = first + 0x00;
   case 8 : first = first + 0x10;
   case 16: first = first + 0x20;
   }

   //add scale
   switch(scale)
   {
   case 0 : first = first + 0x00;
   case 1 : first = first + 0x04;
   case 2 : first = first + 0x08;
   case 3 : first = first + 0x0c;
   }

   // add exponent
   if(exp_len < 4)
   {
        first = first + exp_len - 1;
        appendValue(1,&first);
   }
 else
   {     first = first + 0x03;
         appendValue(1,&first);
	 temp = (BYTE) exp_len;
         appendValue(1, &temp);
   }

        appendValue(exp_len, exponent);
        appendValue(mantissa_len, mantissa);

}

/**
Constructor for any character string encoded as 8-bit characters, i.e. NumericString, PrintableString, VisibleString, etc
The 'type' specifies the type of String as follows:
0 GeneralString
1 NumericString
2 PrintableString
3 VisibleString
4 IA5String
5 TeletexString
6 VideotexString
7 GraphicString
and 'buffer' contains the string value
*/
CharacterStrings::CharacterStrings(int type,int len, const BYTE *buffer):DO(){
	WORD tag[8] = {TAG_GENERALSTRING, TAG_NUMERICSTRING, TAG_PRINTABLESTRING, TAG_VISIBLESTRING, TAG_IA5STRING, TAG_TELETEXSTRING, TAG_VIDEOTEXSTRING, TAG_GRAPHICSTRING};
	setTag(tag[type]);
	setValue(len, buffer);
}
